package co.edu.unal.bioingenium.kbmed.mapping.services.impl;

import co.edu.unal.bioingenium.kbmed.knowledge.mapping.api.Mapping;
import co.edu.unal.bioingenium.kbmed.knowledge.mapping.impl.SoftMapping;
import co.edu.unal.bioingenium.kbmed.knowledge.vo.DescriptorData;
import co.edu.unal.bioingenium.kbmed.mapping.services.api.MappingService;
import co.edu.unal.bioingenium.kbmed.mapping.domain.TextMapping;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jws.WebService;

/**
 * @author  Alejandro Riveros Cruz
 */
@WebService(endpointInterface = "co.edu.unal.bioingenium.kbmed.mapping.services.api.MappingService")
public class MappingServiceImpl implements MappingService {

    /*
     * 
     */
    private MappingQueue mappingQueue;
    /*
     * 
     */
    private Map<String, TextMapping> mappingResults;
    /*
     * 
     */
    private Mapping mapping;
    /*
     * 
     */
    private Random random;

    /**
     * 
     */
    public MappingServiceImpl() {
        random = new Random();
        mappingQueue = new MappingQueue();
        mappingResults = new HashMap<String, TextMapping>();
        mapping = new SoftMapping();
        mapping.init();
        Thread t = new Thread(new Worker());
        t.start();
    }

    /**
     * 
     * @param text
     * @return
     */
    @Override
    public String doMapping(String text) {
        String key = String.valueOf(random.nextInt(1000) + System.currentTimeMillis());
        String textTip;
        if (text.length() > 10) {
            textTip = text.substring(0, 10);
        } else {
            textTip = text;
        }
        Logger.getLogger(MappingServiceImpl.class.getName()).log(Level.INFO, "New mapping request queued {0}={1}...", new Object[]{key, textTip});
        TextMapping textMapping = new TextMapping(text);
        mappingResults.put(key, textMapping);
        mappingQueue.put(key);
        return key;
    }

    public boolean isProcessed(String key) {
        if (mappingResults.containsKey(key) && mappingResults.get(key).getSentences() != null) {
            return true;
        }
        return false;
    }

    /**
     * 
     * @param key
     * @rasdeturn
     */
    @Override
    public Set<String> getSentences(String key) {
        TextMapping textMapping;
        if ((textMapping = mappingResults.get(key)) != null) {
            return textMapping.getSentences();
        }
        return null;
    }

    /**
     *   
     * @param key
     * @param sentence
     * @return
     */
    @Override
    public List<DescriptorData> getConcepts(String key, String sentence) {
        TextMapping textMapping;
        if ((textMapping = mappingResults.get(key)) != null) {
            return textMapping.getConceptIdenfieds(sentence);
        }
        return null;
    }

    /**
     * 
     * @param key
     * @return
     */
    @Override
    public List<DescriptorData> getAllConcepts(String key) {
        TextMapping textMapping;
        if ((textMapping = mappingResults.get(key)) != null) {
            List<DescriptorData> tempList;
            Map<String, DescriptorData> tempMap = new HashMap<String, DescriptorData>();
            Set<String> sentences = textMapping.getSentences();
            double score;
            DescriptorData tempDescriptorData;
            for (String sentence : sentences) {
                tempList = textMapping.getConceptIdenfieds(sentence);
                for (DescriptorData descriptorData : tempList) {
                    if (tempMap.containsKey(descriptorData.getDescriptorId())) {
                        tempDescriptorData = tempMap.get(descriptorData.getDescriptorId());
                        score = tempDescriptorData.getScore();
                        tempDescriptorData.setScore((score + descriptorData.getScore()) / 2d);
                    } else {
                        tempMap.put(descriptorData.getDescriptorId(), descriptorData);
                    }
                }
            }
            List<DescriptorData> globalList = new ArrayList<DescriptorData>(tempMap.values());
            Collections.sort(globalList);
            return globalList;
        }
        return null;
    }

    private class Worker implements Runnable {

        private long cleanUpLimit = 60000;
        private boolean run = true;

        @Override
        public void run() {
            long init = System.currentTimeMillis();
            Map<String, List<DescriptorData>> resultsMap;
            TextMapping textMapping;
            String key;
            while (run) {
                if (!mappingQueue.isEmpty()) {
                    key = mappingQueue.get();
                    Logger.getLogger(Worker.class.getName()).log(Level.INFO, "Perform mapping for {0}", key);
                    if ((textMapping = mappingResults.get(key)) != null) {
                        resultsMap = mapping.doMapping(textMapping.getText());
                        textMapping.setConceptIdenfieds(resultsMap);
                        textMapping.setTimeMillis(System.currentTimeMillis());
                    }
                }
                if (System.currentTimeMillis() - init > 10000) {
                    init = System.currentTimeMillis();
                    cleanOldResults();
                }
            }
        }

        private synchronized void cleanOldResults() {
            String key;
            for (Iterator<String> it = mappingResults.keySet().iterator(); it.hasNext();) {
                key = it.next();
                if ((System.currentTimeMillis() - mappingResults.get(key).getTimeMillis()) > cleanUpLimit) {
                    Logger.getLogger(Worker.class.getName()).log(Level.INFO, "Remove old key {0}", key);
                    it.remove();
                }
            }
        }
    }
}
